#include <stdlib.h>

#include "actions.h"
#include "def_etat_bataille.h"
#include "etat_bataille.h"
#include "etat_partie.h"
#include "resume_saisie.h"
#include "base_donnees.h"
#include "collisions.h"



/**
 * \fn static void incrementer_score_charge(unsigned int* score, const unsigned int vitesse_charge)
 * \brief Augmente un score de charge d'apres les regles fixees par des
 *        constantes et en fonction de la vitesse de l'element concerne
 *
 * \param[in] score L'adresse du score a modifier
 * \param[in] vitesse_charge La vitesse de charge de l'individu concerne
 */
static void incrementer_score_charge(unsigned int* score, const unsigned int vitesse_charge)
{
    *score += MIN_CHARGE + COEFF_CHARGE * vitesse_charge;
}



/**
 * \fn static void calculer_coordonnees_projectile(unsigned int* x_proj,
                                                   unsigned int* y_proj,
                                                   const unsigned char taille_proj,
                                                   const unsigned int x_tireur,
                                                   const unsigned int y_tireur,
                                                   const unsigned char taille_tireur,
                                                   const char direction)
 * \brief Calcule les coordonnees d'apparition d'un projectile (coordonnees de
 *        son coin superieur gauche) en fonction de la direction du tir, de la
 *        position et de la taille du tireur, et de la taille du projectile
 *
 * \param[out] x_proj Adresse de la variable qui recevra la coordonnee horizontale du projectile
 * \param[out] y_proj Adresse de la variable qui recevra la coordonnee verticale du projectile
 * \param[in] taille_proj Taille du projectile qui doit etre tire
 * \param[in] x_tireur Coordonnee horizontale du coin superieur gauche du tireur
 * \param[in] y_tireur Coordonnee verticale du coin superieur gauche du tireur
 * \param[in] taille_tireur Taille de l'individu qui tire
 * \param[in] direction HAUT, DROITE, BAS, ou GAUCHE, suivant la direction du tir
 */
static void calculer_coordonnees_projectile(unsigned int* x_proj,
                                            unsigned int* y_proj,
                                            const unsigned char taille_proj,
                                            const unsigned int x_tireur,
                                            const unsigned int y_tireur,
                                            const unsigned char taille_tireur,
                                            const char direction)
{
    switch(direction)
    {
        case HAUT:
            *x_proj = x_tireur + (taille_tireur + taille_proj % 2 - 1) / 2 - (taille_proj - 1) / 2;
            *y_proj = y_tireur - taille_proj;
            break;
        case DROITE:
            *x_proj = x_tireur + taille_tireur;
            *y_proj = y_tireur + (taille_tireur + taille_proj % 2 - 1) / 2 - (taille_proj - 1) / 2;
            break;
        case BAS:
            *x_proj = x_tireur + (taille_tireur + taille_proj % 2 - 1) / 2 - (taille_proj - 1) / 2 - (taille_proj + taille_tireur) % 2;
            *y_proj = y_tireur + taille_tireur;
            break;
        case GAUCHE:
            *x_proj = x_tireur - taille_proj;
            *y_proj = y_tireur + (taille_tireur + taille_proj % 2 - 1) / 2 - (taille_proj - 1) / 2 - (taille_proj + taille_tireur) % 2;
            break;
        default:
            break;
    }
}



/**
 * \fn static char choisir_direction_tir(const struct ennemi* enn,
                                         const struct etat_bataille* bataille,
                                         const struct carte_zone* carte,
                                         const struct base_donnees* donnees)
 * \brief Donne la direction de tir choisie par un ennemi
 *
 * \param[in] enn Un pointeur vers l'ennemi considere
 * \param[in] bataille L'adresse de l'etat de la bataille en cours
 * \param[in] carte L'adresse de la carte de la zone actuelle
 * \param[in] donnees L'adresse d'une base de donnees correctement remplie
 * \return HAUT, DROITE, BAS, ou GAUCHE
 */
static char choisir_direction_tir(const struct ennemi* enn,
                                  const struct etat_bataille* bataille,
                                  const struct carte_zone* carte,
                                  const struct base_donnees* donnees)
{
    int diff_h, diff_v;

    diff_h = bataille->coord_j_x + DECALAGE_X_J - enn->coord_x;
    diff_v = bataille->coord_j_y + DECALAGE_Y_J - enn->coord_y;
    if(abs(diff_h) > abs(diff_v))
    {
        if(diff_h < 0)
            return GAUCHE;
        else
            return DROITE;
    }
    else
    {
        if(diff_v < 0)
            return HAUT;
        else
            return BAS;
    }
}



void faire_tirer_joueur(struct carte_zone* carte,
                        struct etat_bataille* bataille,
                        const struct etat_partie* partie,
                        const struct resume_saisie* saisie,
                        const struct base_donnees* donnees)
{
    char direction;
    unsigned int coord_apparition_x, coord_apparition_y;
    unsigned char taille_projectile;

    /*
     * Le tir marche comme le deplacement : incrementation d'un score
     * jusqu'a un seuil, puis retrait de ce seuil au score. Voir les
     * commentaires de deplacer_joueur pour plus d'infos.
     */
    direction = saisie->direction_tir;
    if(bataille->score_charge_j < SEUIL_CHARGE)
        incrementer_score_charge(&bataille->score_charge_j, get_vitesse_charge_temp(bataille));
    if(direction != NEUTRE)
    {
        if(bataille->score_charge_j >= SEUIL_CHARGE)
        {
            /* le joueur tire ! */
            taille_projectile = get_taille_projectile_par_id(donnees,
                                get_id_projectile_objet(donnees, partie->arme));
            calculer_coordonnees_projectile(&coord_apparition_x, &coord_apparition_y,
                                            taille_projectile,
                                            bataille->coord_j_x + DECALAGE_X_J,
                                            bataille->coord_j_y + DECALAGE_Y_J,
                                            TAILLE_JOUEUR, direction);
            ajouter_projectile(&bataille->projectiles,
                               get_id_projectile_objet(donnees, partie->arme),
                               get_puissance_temp(bataille), direction,
                               coord_apparition_x, coord_apparition_y, VRAI);
            gerer_apparition_projectile(carte, bataille, DISTANCE, donnees);
            bataille->score_charge_j -= SEUIL_CHARGE;
        }
    }
}




void faire_tirer_ennemis(struct carte_zone* carte,
                         struct etat_bataille* bataille,
                         const struct base_donnees* donnees)
{
    char direction;
    unsigned int coord_apparition_x, coord_apparition_y;
    unsigned char taille_projectile;
    struct ennemi* p_ennemi;

    /*
     * Le tir marche comme le deplacement : incrementation d'un score
     * jusqu'a un seuil, puis retrait de ce seuil au score. Voir les
     * commentaires de deplacer_joueur pour plus d'infos.
     */
     p_ennemi = bataille->ennemis.prem;
     while(p_ennemi)
     {
        if(p_ennemi->score_charge < SEUIL_CHARGE)
            incrementer_score_charge(&p_ennemi->score_charge, get_vit_charge(donnees, p_ennemi));
        if(p_ennemi->score_charge >= SEUIL_CHARGE)
        {
            direction = choisir_direction_tir(p_ennemi, bataille, carte, donnees);
            taille_projectile = get_taille_projectile_par_id(donnees,
                                get_id_projectile(donnees, p_ennemi));
            calculer_coordonnees_projectile(&coord_apparition_x, &coord_apparition_y,
                                            taille_projectile,
                                            p_ennemi->coord_x,
                                            p_ennemi->coord_y,
                                            get_taille(donnees, p_ennemi),
                                            direction);
            ajouter_projectile(&bataille->projectiles,
                               get_id_projectile(donnees, p_ennemi),
                               get_puissance(donnees, p_ennemi), direction,
                               coord_apparition_x, coord_apparition_y, FAUX);
            gerer_apparition_projectile(carte, bataille, get_type_attaque(donnees, p_ennemi), donnees);
            p_ennemi->score_charge -= SEUIL_CHARGE;
            p_ennemi->orientation = direction;
        }
        p_ennemi = p_ennemi->suiv;
     }
}



void utiliser_objet(struct etat_bataille* bataille,
                           struct etat_partie* partie,
                           const struct resume_saisie* saisie,
                           const struct base_donnees* donnees)
{
    int id_objet;
    unsigned int pv_max_temporaires;

    if((id_objet = partie->objets_utilisables[saisie->utilisation_objet - 1])
                 == VIDE)
        return;
    partie->objets_utilisables[saisie->utilisation_objet - 1] = VIDE;
    /* soin avec verification de respect du maximum de points de vie */
    pv_max_temporaires = get_pv_max_temp(bataille);
    if((bataille->pv_j += get_pv_objet(donnees, id_objet))
            > pv_max_temporaires)
        bataille->pv_j = pv_max_temporaires;
    bataille->stats_temp.vitesse_deplacement
            += get_vitesse_deplacement_objet(donnees, id_objet);
    bataille->stats_temp.puissance
            += get_puissance_objet(donnees, id_objet);
    bataille->stats_temp.vitesse_charge
            += get_vitesse_charge_objet(donnees, id_objet);
}



